package com.jt.common.util;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;


public abstract class BeanUtils extends org.springframework.beans.BeanUtils {

    private static final ConcurrentMap<String, HashMap<String, String>> fieldNameCache = new ConcurrentHashMap<>();

    private static final Logger logger = LoggerFactory.getLogger(BeanUtils.class);

    /**
     * @return : void
     * @Description : 拷贝属性（忽略类型不一致而名称相同的属性）
     * @Creation Date : 2015-1-28 上午9:44:18
     * @Author : wangchao
     */
    public static void copy(Object source, Object target) {
        copy(source, target, false);
    }

    /**
     * @return : void
     * @Description : 拷贝属性（忽略类型不一致的属性），指定ignoreNull参数是否忽略为null的属性
     * @Creation Date : 2015-3-22 下午12:34:57
     * @Author : wangchao
     */
    public static void copy(Object source, Object target, boolean ignoreNull) {
        BeanWrapper sourceBean = new BeanWrapperImpl(source);
        BeanWrapper targetBean = new BeanWrapperImpl(target);
        PropertyDescriptor[] targetProperties = getPropertyDescriptors(target.getClass());
        for (PropertyDescriptor property : targetProperties) {
            Class<?> targetType = property.getPropertyType();
            String name = property.getName();
            PropertyDescriptor sourceProperty = getPropertyDescriptor(source.getClass(), name);
            if (sourceProperty == null)
                continue;
            Object propertyVal = sourceBean.getPropertyValue(property.getName());
            if (!targetType.equals(sourceProperty.getPropertyType()) || "class".equals(property.getName())
                    || propertyVal == null || (ignoreNull && propertyVal == null))
                continue;
            targetBean.setPropertyValue(property.getName(), sourceBean.getPropertyValue(property.getName()));
        }
    }

    /**
     * @return : String[]
     * @Description : 获取目标对象的所有属性列表
     * @Creation Date : 2016年6月2日 上午11:38:49
     * @Author : wangchao
     */
    public static Map<String, String> getFieldNames(Object target) {
        if (target == null)
            return null;
        HashMap<String, String> fieldNames = null;
        fieldNames = fieldNameCache.get(target.getClass().getName());
        if (!CollectionUtils.isEmpty(fieldNames))
            return fieldNames;
        fieldNames = new HashMap<>();
        PropertyDescriptor[] targetProperties = getPropertyDescriptors(target.getClass());
        for (PropertyDescriptor property : targetProperties) {
            if ("class".equals(property.getName()))
                continue;
            fieldNames.put(property.getName(), property.getName());
        }
        fieldNameCache.putIfAbsent(target.getClass().getName(), fieldNames);
        return fieldNames;
    }

    /**
     * @return : Map<String,Object>
     * @Description : 将对象转为Map
     * @Creation Date : 2015-2-12 下午1:33:31
     * @Author : wangchao
     */
    public static Map<String, Object> toMap(Object target) {
        return toMap(target, null, false);
    }

    public static Map<String, Object> toMap(Object target, boolean isNull) {
        return toMap(target, null, isNull);
    }

    /**
     * @return : Map<String,Object>
     * @Description : 将目标对象target转为Map格式,但将excludeProperties集合指定的属性排除在外
     * @Creation Date : 2015-2-12 下午1:46:14
     * @Author : wangchao
     */
    public static Map<String, Object> toMap(Object target, List<String> excludeProperties, boolean isNull) {
        if (target == null)
            throw new RuntimeException("对象不能为空！");
        BeanWrapper bean = new BeanWrapperImpl(target);
        PropertyDescriptor[] targetProperties = BeanUtils.getPropertyDescriptors(target.getClass());
        Map<String, Object> map = new HashMap<String, Object>();
        for (PropertyDescriptor property : targetProperties) {
            Object propetyValue = bean.getPropertyValue(property.getName());
            if (isNull && propetyValue == null) {
                map.put(property.getName(), "");
                continue;
            }
            if (propetyValue == null || isExcludeProperty(excludeProperties, property.getName()))
                continue;
            if (propetyValue instanceof String) { // 处理字符串类型
                if (StringUtils.hasText(propetyValue.toString())) {
                    propetyValue = propetyValue.toString().trim();
                } else {
                    continue;
                }
            }
            map.put(property.getName(), propetyValue);
        }
        return map;
    }

    /**
     * @return : boolean
     * @Description : 是否排除该属性
     * @Creation Date : 2015-2-12 下午1:41:52
     * @Author : wangchao
     */
    private static boolean isExcludeProperty(List<String> excludeProperties, String propertyName) {

        if ("class".equals(propertyName)) // 如果当前属性名称等于class,则排除
            return true;

        if (CollectionUtils.isEmpty(excludeProperties))
            return false;

        for (String property : excludeProperties) { // 如果当前属性名称在可排除集合之内,则返回true
            if (property.equals(propertyName))
                return true;
        }
        return false;
    }

    public static NameValuePair[] toFormParam(Object source) throws Exception {
        return toFormParam(source, null);
    }

    public static NameValuePair[] toFormParam(Object source, String[] excludeProperties) throws Exception {
        BeanWrapper sourceBean = new BeanWrapperImpl(source);
        PropertyDescriptor[] properties = getPropertyDescriptors(source.getClass());
        ArrayList<NameValuePair> result = new ArrayList<NameValuePair>();
        for (PropertyDescriptor property : properties) {
            Object propetyValue = sourceBean.getPropertyValue(property.getName());
            if (propetyValue == null)
                continue;
            if (propetyValue instanceof String) { // 处理字符串类型
                if (StringUtils.hasText(propetyValue.toString())) {
                    propetyValue = propetyValue.toString().trim();
                } else {
                    continue;
                }
            }
            result.add(new BasicNameValuePair(property.getName(), propetyValue.toString()));
        }
        if (!CollectionUtils.isEmpty(result)) {
            NameValuePair[] names = new NameValuePair[result.size()];
            for (int i = 0; i < result.size(); i++) {
                names[i] = result.get(i);
            }
            return names;
        }
        return null;
    }

    /**
     * @return : String
     * @Description : 将表对象转为get请求参数
     * @Creation Date : 2016-3-16 上午1:46:17
     * @Author : wangchao
     */
    public static String toRequestByGet(Object target) {
        if (target == null)
            return null;
        BeanWrapper sourceBean = new BeanWrapperImpl(target);
        PropertyDescriptor[] properties = getPropertyDescriptors(target.getClass());
        StringBuilder builder = new StringBuilder();
        boolean flag = false;
        for (int i = 0; i < properties.length; i++) {
            PropertyDescriptor property = properties[i];
            Object propetyValue = sourceBean.getPropertyValue(property.getName());
            if (propetyValue == null || property.getName().equals("class"))
                continue;

            if (propetyValue instanceof String) // 处理字符串类型
                if (StringUtils.hasText(propetyValue.toString()))
                    propetyValue = propetyValue.toString().trim();
            if (propetyValue.getClass().equals(Date.class))
                propetyValue = DateUtil.format((Date) propetyValue, DateUtil.DATE_HH_MM_SS);
            builder.append(!flag ? "?" : "&").append(property.getName()).append("=").append(propetyValue.toString());
            flag = true;
        }
        return builder.toString();
    }

    /**
     * @return : boolean
     * @Description : 判断一个对象是否为空；首先这个对象不能空,并且对象中的属性至少有一个不为空,则认为这个对象不为空，返回true,
     * 否则返回false
     * @Creation Date : 2016年6月6日 下午7:02:29
     * @Author : wangchao
     */
    public static boolean isEmpty(Object target) {
        return !isNotEmpty(target);
    }

    public static boolean isNotEmpty(Object target) {
        if (target == null) {
            return false;
        }
        BeanWrapper targetBean = new BeanWrapperImpl(target);
        PropertyDescriptor[] targetProperties = getPropertyDescriptors(target.getClass());
        for (PropertyDescriptor property : targetProperties) {
            Object propertyVal = targetBean.getPropertyValue(property.getName());
            if ("class".equals(property.getName()))
                continue;
            // 如果属性不为空,且属性类型为字符串类型且为有效字符串时，返回true
            if (propertyVal != null
                    && (propertyVal instanceof String && StringUtils.hasText((CharSequence) propertyVal)))
                return true;
            // 否则属性不为空,返回true,否则返回false
            if (propertyVal != null && !(propertyVal instanceof String))
                return true;
        }
        return false;
    }

    /**
     * @return : String
     * @Description    : wangchao 方法原文描述
     * @Creation Date  : 2016年7月7日 下午5:59:38
     * @Author : wangchao
     */
    public static String toString(Object target) {
        if (target == null)
            return "";
        try {
            BeanWrapper targetBean = new BeanWrapperImpl(target);
            PropertyDescriptor[] targetProperties = getPropertyDescriptors(target.getClass());
            StringBuilder builder = new StringBuilder();
            builder.append(target.getClass().getName()).append(":[");
            for (PropertyDescriptor property : targetProperties) {
                String name = property.getName();
                PropertyDescriptor targetProperty = getPropertyDescriptor(target.getClass(), name);
                if (targetProperty == null || "class".equals(name))
                    continue;
                Object propertyVal = targetBean.getPropertyValue(name);
                builder.append(name).append(":").append(propertyVal == null ? "" : propertyVal).append(",");
            }
            builder.append("]");
            return builder.toString();
        } catch (BeansException e) {
            logger.warn("打印目标对象的值失败!");
        }
        return "";
    }

    /**
     * 将实体类对象装成HashMap
     *
     * @param obj
     * @return
     */
    public static HashMap<String, Object> transBean2Map(Object obj) {

        if (obj == null) {
            return null;
        }
        HashMap<String, Object> map = new HashMap<String, Object>();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();

                // 过滤class属性  
                if (!key.equals("class")) {
                    // 得到property对应的getter方法  
                    Method getter = property.getReadMethod();
                    Object value = getter.invoke(obj);

                    map.put(key, value);
                }

            }
        } catch (Exception e) {
            System.out.println("transBean2Map Error " + e);
        }
        return map;
    }

    /**
     * 利用序列化深度克隆
     *
     * @param src
     * @return
     * @throws Exception
     */
    public static Object clone(Object src) throws Exception {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(src);
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        Object newObj = in.readObject();
        return newObj;
    }
}
